package com.jrauto.CarAppBackend.service.impl;

/**
 * @author : huiMing
 * Date : 2025年09月12日 10:54
 * @version V1.0
 */

import com.jrauto.CarAppBackend.entity.UserOnlineLog;
import com.jrauto.CarAppBackend.mapper.UserOnlineLogMapper;
import com.jrauto.CarAppBackend.service.UserOnlineService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户在线状态服务实现类
 * 使用Redis作为主要存储，数据库记录历史日志
 */
@Slf4j
@Service
public class UserOnlineServiceImpl implements UserOnlineService {

    private static final String ONLINE_USER_KEY = "chat:online:user:";
    private static final String ONLINE_USERS_SET = "chat:online:users";
    private static final Duration ONLINE_EXPIRE_TIME = Duration.ofMinutes(30);

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private UserOnlineLogMapper userOnlineLogMapper;

    @Override
    public void userOnline(Integer userId, String ipAddress) {
        try {
            // 1. Redis记录实时在线状态
            String userKey = ONLINE_USER_KEY + userId;
            Map<String, Object> onlineInfo = new HashMap<>();
            onlineInfo.put("userId", userId);
            onlineInfo.put("onlineTime", System.currentTimeMillis());
            onlineInfo.put("ipAddress", ipAddress);
            onlineInfo.put("lastHeartbeat", System.currentTimeMillis());

            // 存储用户在线信息
            redisTemplate.opsForHash().putAll(userKey, onlineInfo);
            redisTemplate.expire(userKey, ONLINE_EXPIRE_TIME);

            // 添加到在线用户集合
            redisTemplate.opsForSet().add(ONLINE_USERS_SET, userId.toString());

            // 2. 异步记录到数据库日志
            recordOnlineLogAsync(userId, ipAddress);

            log.info("用户 {} 上线成功，IP: {}", userId, ipAddress);

        } catch (Exception e) {
            log.error("用户上线处理失败: userId={}, error={}", userId, e.getMessage(), e);
        }
    }

    @Override
    public void userOffline(Integer userId) {
        try {
            // 1. 从Redis移除在线状态
            String userKey = ONLINE_USER_KEY + userId;

            // 获取上线时间用于计算在线时长
            Object onlineTimeObj = redisTemplate.opsForHash().get(userKey, "onlineTime");

            redisTemplate.delete(userKey);
            redisTemplate.opsForSet().remove(ONLINE_USERS_SET, userId.toString());

            // 2. 异步更新数据库下线时间
            if (onlineTimeObj != null) {
                updateOfflineLogAsync(userId);
            }

            log.info("用户 {} 下线成功", userId);

        } catch (Exception e) {
            log.error("用户下线处理失败: userId={}, error={}", userId, e.getMessage(), e);
        }
    }

    @Override
    public boolean isUserOnline(Integer userId) {
        try {
            return redisTemplate.hasKey(ONLINE_USER_KEY + userId);
        } catch (Exception e) {
            log.error("检查用户在线状态失败: userId={}, error={}", userId, e.getMessage());
            return false;
        }
    }

    @Override
    public Set<String> getOnlineUserIds() {
        try {
            Set<Object> members = redisTemplate.opsForSet().members(ONLINE_USERS_SET);
            if (members == null) {
                return new HashSet<>();
            }
            return members.stream()
                    .map(Object::toString)
                    .collect(Collectors.toSet());
        } catch (Exception e) {
            log.error("获取在线用户列表失败: {}", e.getMessage());
            return new HashSet<>();
        }
    }

    @Override
    public long getOnlineUserCount() {
        try {
            Long count = redisTemplate.opsForSet().size(ONLINE_USERS_SET);
            return count != null ? count : 0L;
        } catch (Exception e) {
            log.error("获取在线用户数量失败: {}", e.getMessage());
            return 0L;
        }
    }

    @Override
    public Map<String, Object> getUserOnlineInfo(Integer userId) {
        try {
            String userKey = ONLINE_USER_KEY + userId;
            Map<Object, Object> info = redisTemplate.opsForHash().entries(userKey);

            if (info.isEmpty()) {
                return null;
            }

            // 转换为String key的Map
            return info.entrySet().stream()
                    .collect(Collectors.toMap(
                            entry -> entry.getKey().toString(),
                            Map.Entry::getValue
                    ));
        } catch (Exception e) {
            log.error("获取用户在线信息失败: userId={}, error={}", userId, e.getMessage());
            return null;
        }
    }

    @Override
    public Map<Integer, Boolean> batchCheckOnlineStatus(List<Integer> userIds) {
        Map<Integer, Boolean> result = new HashMap<>();

        try {
            for (Integer userId : userIds) {
                result.put(userId, isUserOnline(userId));
            }
        } catch (Exception e) {
            log.error("批量检查在线状态失败: {}", e.getMessage());
            // 返回全部离线状态
            userIds.forEach(userId -> result.put(userId, false));
        }

        return result;
    }

    @Override
    public List<UserOnlineLog> getUserOnlineHistory(Integer userId, int limit) {
        try {
            return userOnlineLogMapper.selectUserOnlineHistory(userId, limit);
        } catch (Exception e) {
            log.error("获取用户在线历史失败: userId={}, error={}", userId, e.getMessage());
            return new ArrayList<>();
        }
    }

    @Override
    public List<Object> getOnlineStatistics(LocalDateTime startTime, LocalDateTime endTime) {
        try {
            return userOnlineLogMapper.getOnlineStatistics(startTime, endTime);
        } catch (Exception e) {
            log.error("获取在线统计数据失败: {}", e.getMessage());
            return new ArrayList<>();
        }
    }

    @Override
    public void refreshUserOnlineStatus(Integer userId) {
        try {
            String userKey = ONLINE_USER_KEY + userId;
            if (redisTemplate.hasKey(userKey)) {
                // 更新最后心跳时间
                redisTemplate.opsForHash().put(userKey, "lastHeartbeat", System.currentTimeMillis());
                // 延长过期时间
                redisTemplate.expire(userKey, ONLINE_EXPIRE_TIME);
            }
        } catch (Exception e) {
            log.error("刷新用户在线状态失败: userId={}, error={}", userId, e.getMessage());
        }
    }

    /**
     * 异步记录用户上线日志
     */
    @Async
    protected void recordOnlineLogAsync(Integer userId, String ipAddress) {
        try {
            UserOnlineLog log = new UserOnlineLog();
            log.setUserId(userId);
            log.setOnlineTime(LocalDateTime.now());
            log.setIpAddress(ipAddress);

            userOnlineLogMapper.insert(log);
        } catch (Exception e) {
            log.error("记录用户上线日志失败: userId={}, error={}", userId, e.getMessage());
        }
    }

    /**
     * 异步更新用户下线日志
     */
    @Async
    protected void updateOfflineLogAsync(Integer userId) {
        try {
            userOnlineLogMapper.updateOfflineTime(userId, LocalDateTime.now());
        } catch (Exception e) {
            log.error("更新用户下线日志失败: userId={}, error={}", userId, e.getMessage());
        }
    }

    /**
     * 定时清理过期的在线状态
     * 每5分钟执行一次，清理Redis中超时的用户
     */
    @Scheduled(fixedRate = 300000) // 5分钟
    public void cleanExpiredOnlineStatus() {
        try {
            Set<String> onlineUsers = getOnlineUserIds();
            if (onlineUsers == null || onlineUsers.isEmpty()) {
                return;
            }

            long currentTime = System.currentTimeMillis();
            long timeoutThreshold = ONLINE_EXPIRE_TIME.toMillis();

            for (String userIdStr : onlineUsers) {
                try {
                    String userKey = ONLINE_USER_KEY + userIdStr;
                    Object lastHeartbeatObj = redisTemplate.opsForHash().get(userKey, "lastHeartbeat");

                    if (lastHeartbeatObj != null) {
                        long lastHeartbeat = Long.parseLong(lastHeartbeatObj.toString());

                        // 如果超过超时时间，标记为离线
                        if (currentTime - lastHeartbeat > timeoutThreshold) {
                            Integer userId = Integer.parseInt(userIdStr);
                            userOffline(userId);
                            log.info("清理超时用户: {}", userId);
                        }
                    }
                } catch (Exception e) {
                    log.error("清理用户在线状态失败: userId={}, error={}", userIdStr, e.getMessage());
                }
            }
        } catch (Exception e) {
            log.error("定时清理在线状态任务失败: {}", e.getMessage());
        }
    }

    /**
     * 定时清理过期的数据库日志
     * 每天凌晨2点执行，清理30天前的日志
     */
    @Override
    @Scheduled(cron = "0 0 2 * * ?")
    public void cleanExpiredData() {
        try {
            LocalDateTime expireTime = LocalDateTime.now().minusDays(30);
            int deletedCount = userOnlineLogMapper.cleanExpiredLogs(expireTime);
            log.info("清理过期在线日志完成，删除记录数: {}", deletedCount);
        } catch (Exception e) {
            log.error("清理过期数据失败: {}", e.getMessage());
        }
    }
}